package org.oslims.server.service.impl;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Set;

import net.sf.gilead.gwt.GwtConfigurationHelper;
import net.sf.gilead.gwt.PersistentRemoteService;

import org.hibernate.Session;
import org.oslims.client.service.SampleService;
import org.oslims.domain.AbstractSample;
import org.oslims.domain.Analysis;
import org.oslims.domain.AnalysisResult;
import org.oslims.domain.Mixture;
import org.oslims.domain.MixtureComponent;
import org.oslims.domain.ParameterResult;
import org.oslims.domain.Request;
import org.oslims.domain.Sample;
import org.oslims.server.dao.AbstractSampleDao;
import org.oslims.server.dao.AnalysisResultDao;
import org.oslims.server.dao.MixtureComponentDao;
import org.oslims.server.dao.MixtureDao;
import org.oslims.server.dao.ParameterResultDao;
import org.oslims.server.dao.SampleDao;
import org.oslims.server.dao.hbm.HbmAbstractSampleDao;
import org.oslims.server.dao.hbm.HbmAnalysisResultDao;
import org.oslims.server.dao.hbm.HbmMixtureComponentDao;
import org.oslims.server.dao.hbm.HbmMixtureDao;
import org.oslims.server.dao.hbm.HbmParameterResultDao;
import org.oslims.server.dao.hbm.HbmSampleDao;
import org.oslims.server.dao.util.HibernateUtil;

import com.google.gwt.validation.client.InvalidConstraint;
import com.google.gwt.validation.client.interfaces.IValidator;
import com.google.gwt.validation.server.ServerValidator;

public class SampleServiceImpl extends PersistentRemoteService implements SampleService {

	/**
	 * 
	 */
	private static final long serialVersionUID = -4464139023786525836L;

	SampleDao sampleDao;
	MixtureDao mixtureDao;
	AbstractSampleDao abstractSampleDao;
	ParameterResultDao parameterResultDao;
	AnalysisResultDao analysisResultDao;

	MixtureComponentDao mixtureComponentDao;

	IValidator<Sample> sampleValidator = new ServerValidator<Sample>();
	IValidator<Mixture> mixtureValidator = new ServerValidator<Mixture>();

	public SampleServiceImpl() {
		sampleDao = new HbmSampleDao();
		mixtureDao = new HbmMixtureDao();
		mixtureComponentDao = new HbmMixtureComponentDao();
		abstractSampleDao = new HbmAbstractSampleDao();
		parameterResultDao = new HbmParameterResultDao();
		analysisResultDao = new HbmAnalysisResultDao();

		net.sf.gilead.core.hibernate.HibernateUtil hibernateUtil = new net.sf.gilead.core.hibernate.HibernateUtil(HibernateUtil.getSessionFactory());
		setBeanManager(GwtConfigurationHelper.initGwtStatelessBeanManager(hibernateUtil));
	}

	@Override
	public void saveOrUpdateSample(Sample sample) {

		Set<InvalidConstraint<Sample>> violations = sampleValidator.validate(sample);

		if (violations.size() == 0) { // Only process the entity if valid!!!
			Session session = HibernateUtil.getSession();
			session.beginTransaction();
			try {
				sampleDao.setSession(session);

				if (sample.getId() == null) { // Generate the code for a new
					// request
					Sample lastSample = sampleDao.findLastSample();
					Calendar calendar = Calendar.getInstance();
					Integer year = calendar.get(Calendar.YEAR);
					year = Integer.parseInt(year.toString());

					NumberFormat prefix = new DecimalFormat("0000");
					NumberFormat general = new DecimalFormat("00000");

					String newCode = "";
					if (lastSample == null) {
						newCode = prefix.format(year) + "0001";
					} else {
						Integer lastYear = Integer.parseInt(lastSample.getCode().substring(0, 4));
						if (lastYear < year) {
							newCode = prefix.format(year) + "0001";
						} else {
							Integer code = Integer.parseInt(lastSample.getCode());
							code = code + 1;
							newCode = general.format(code);
						}
					}

					sample.setCode(newCode);
				}

				sampleDao.makePersistent(sample);
				session.getTransaction().commit();
			} catch (RuntimeException e) {
				session.getTransaction().rollback();
				throw e;
			} finally {
				session.close();
			}
		}
	}

	@Override
	public List<Sample> getSamplesByRequest(Request request) {

		Session session = HibernateUtil.getSession();
		sampleDao.setSession(session);

		List<Sample> result = sampleDao.findByRequestCode(request.getCode());

		session.close();

		return result;
	}

	@Override
	public void markSamplesAsRecieved(List<Sample> samples, String storagePlace) {
		Session session = HibernateUtil.getSession();
		session.beginTransaction();
		try {
			sampleDao.setSession(session);

			for (Sample s : samples) {

				Sample currSample = sampleDao.findById(s.getId(), false);
				currSample.setStoragePlace(storagePlace);

				if (currSample.getAnalysisResults().size() > 0) {
					currSample.setState("In Analyzing");
				} else {
					currSample.setState("Recieved");
				}

				sampleDao.makePersistent(currSample);
			}

			session.getTransaction().commit();
		} catch (RuntimeException e) {
			session.getTransaction().rollback();
			throw e;
		} finally {
			session.close();
		}
	}

	@Override
	public List<Sample> searchSamples(String sampleCode, String description, String origin, String requestCode, String sampleType, String storagePlace) {

		Session session = HibernateUtil.getSession();
		sampleDao.setSession(session);

		List<Sample> result = sampleDao.searchByProperties(sampleCode, description, origin, requestCode, sampleType, storagePlace);

		session.close();

		return result;
	}

	@Override
	public void importSamplesForRequest(List<Sample> samples, Request request) {
		Session session = HibernateUtil.getSession();
		session.beginTransaction();
		try {
			sampleDao.setSession(session);

			for (Sample s : samples) {
				if (!s.getRequests().contains(request))
					s.getRequests().add(request);
				sampleDao.makePersistent(s);
			}

			session.getTransaction().commit();
		} catch (RuntimeException e) {
			session.getTransaction().rollback();
			throw e;
		} finally {
			session.close();
		}
	}

	@Override
	public Integer getSampleCount(Request request) {
		Session session = HibernateUtil.getSession();
		sampleDao.setSession(session);

		List<Sample> result = sampleDao.findByRequestCode(request.getCode());
		List<Sample> resultWithoutMixtures = new ArrayList<Sample>();

		for (Sample s : result) {
			if (!(s instanceof Mixture)) {
				resultWithoutMixtures.add(s);
			}
		}
		session.close();

		return resultWithoutMixtures.size();
	}

	@Override
	public List<Sample> getSamplesByRequest(Request request, int start, int length, String sortBy, boolean desc) {
		Session session = HibernateUtil.getSession();
		sampleDao.setSession(session);

		List<Sample> result = sampleDao.findByRequestCode(request.getCode(), start, length, sortBy, desc);
		List<Sample> resultWithoutMixtures = new ArrayList<Sample>();

		for (Sample s : result) {
			if (!(s instanceof Mixture)) {
				resultWithoutMixtures.add(s);
			}
		}
		session.close();

		return resultWithoutMixtures;
	}

	@Override
	public void saveOrUpdateMixture(Mixture mixture) {

		Set<InvalidConstraint<Mixture>> violations = mixtureValidator.validate(mixture);

		if (violations.size() == 0) { // Only process the entity if valid!!!
			Session session = HibernateUtil.getSession();
			session.beginTransaction();
			try {
				mixtureDao.setSession(session);
				mixtureComponentDao.setSession(session);

				if (mixture.getId() == null) { // Generate the code for a new
					// request
					Mixture lastMixture = mixtureDao.findLastMixture();
					Calendar calendar = Calendar.getInstance();
					Integer year = calendar.get(Calendar.YEAR);
					year = Integer.parseInt(year.toString());

					NumberFormat prefix = new DecimalFormat("0000");
					NumberFormat general = new DecimalFormat("00000");

					String newCode = "";
					if (lastMixture == null) {
						newCode = "M" + prefix.format(year) + "0001";
					} else {
						Integer lastYear = Integer.parseInt(lastMixture.getCode().substring(1, 5));
						if (lastYear < year) {
							newCode = "M" + prefix.format(year) + "0001";
						} else {
							Integer code = Integer.parseInt(lastMixture.getCode().substring(1, lastMixture.getCode().length()));
							code = code + 1;
							newCode = "M" + general.format(code);
						}
					}

					mixture.setCode(newCode);
				}

				mixtureDao.makePersistent(mixture);

				mixtureComponentDao.removeAllComponentsForMixture(mixture);

				for (MixtureComponent mc : mixture.getComponents()) {
					mc.setRootSample(mixture);
					mixtureComponentDao.makePersistent(mc);
				}
				session.getTransaction().commit();
			} catch (RuntimeException e) {
				session.getTransaction().rollback();
				throw e;
			} finally {
				session.close();
			}
		}
	}

	@Override
	public List<Mixture> getAllMixturesByRequest(Request request) {

		Session session = HibernateUtil.getSession();
		mixtureDao.setSession(session);

		List<Mixture> result = mixtureDao.findByRequestCode(request.getCode());

		session.close();

		return result;
	}

	@Override
	public void markMixturesAsPrepared(List<Mixture> mixtures, String storagePlace) {
		Session session = HibernateUtil.getSession();
		session.beginTransaction();
		try {
			mixtureDao.setSession(session);

			for (Mixture m : mixtures) {
				Mixture currMixture = mixtureDao.findById(m.getId(), false);
				currMixture.setStoragePlace(storagePlace);

				if (currMixture.getAnalysisResults().size() > 0) {
					currMixture.setState("In Analyzing");
				} else {
					currMixture.setState("Prepared");
				}
				mixtureDao.makePersistent(currMixture);
			}

			session.getTransaction().commit();
		} catch (RuntimeException e) {
			session.getTransaction().rollback();
			throw e;
		} finally {
			session.close();
		}
	}

	@Override
	public List<Mixture> getAllMixturesByRequest(Request request, int start, int length, String sortBy, boolean desc) {
		Session session = HibernateUtil.getSession();
		mixtureDao.setSession(session);

		List<Mixture> result = mixtureDao.findByRequestCode(request.getCode(), start, length, sortBy, desc);

		for (Mixture m : result) {
			System.out.println(m.getCode());
			for (MixtureComponent mc : m.getComponents()) {
				System.out.println("   " + mc.getRootSample());
			}
		}
		session.close();

		return result;
	}

	@Override
	public Integer getMixtureCount(Request request) {
		Session session = HibernateUtil.getSession();
		mixtureDao.setSession(session);

		Integer result = mixtureDao.getEntityCount();

		session.close();

		return result;
	}

	@Override
	public void importMixtureForRequest(List<Mixture> mixture, Request request) {
		Session session = HibernateUtil.getSession();
		session.beginTransaction();
		try {
			mixtureDao.setSession(session);

			for (Mixture m : mixture) {
				if (!m.getRequests().contains(request))
					m.getRequests().add(request);
				mixtureDao.makePersistent(m);
			}

			session.getTransaction().commit();
		} catch (RuntimeException e) {
			session.getTransaction().rollback();
			throw e;
		} finally {
			session.close();
		}
	}

	@Override
	public List<AbstractSample> getAllSamplesByRequest(Request request) {

		Session session = HibernateUtil.getSession();
		abstractSampleDao.setSession(session);

		List<AbstractSample> result = abstractSampleDao.findByRequestCode(request.getCode());

		session.close();

		return result;
	}

	@Override
	public List<AbstractSample> searchAllSamples(String sampleCode, String description, String origin, String requestCode, String sampleType, String storagePlace) {

		Session session = HibernateUtil.getSession();
		abstractSampleDao.setSession(session);

		List<AbstractSample> result = abstractSampleDao.searchByProperties(sampleCode, description, origin, requestCode, sampleType, storagePlace);

		session.close();

		return result;
	}

	@Override
	public List<Mixture> searchMixtures(String sampleCode, String description, String origin, String requestCode, String sampleType, String storagePlace) {

		Session session = HibernateUtil.getSession();
		mixtureDao.setSession(session);

		List<Mixture> result = mixtureDao.searchByProperties(sampleCode, description, origin, requestCode, sampleType, storagePlace);

		session.close();

		return result;
	}

	@Override
	public List<AbstractSample> getSamplesByAnalysisAndState(Analysis analysis, String state) {
		Session session = HibernateUtil.getSession();
		abstractSampleDao.setSession(session);

		List<AbstractSample> result = abstractSampleDao.findByAnalysisAndState(analysis.getId(), state);

		session.close();
		return result;
	}

	@Override
	public List<AbstractSample> getAllSamplesReadyForAnalysisByRequest(Request request) {
		Session session = HibernateUtil.getSession();
		abstractSampleDao.setSession(session);

		List<AbstractSample> result = abstractSampleDao.findReadyForAnalysisByRequestCode(request.getCode());

		session.flush();
		session.close();
		return result;
	}

	@Override
	public void validateSampleResults(List<AbstractSample> samples) {
		if (samples.size() == 0) {
			throw new IllegalArgumentException("No results provided to validate");
		}

		String newState = samples.get(0).getState();

		Session session = HibernateUtil.getSession();

		session.beginTransaction();

		parameterResultDao.setSession(session);
		analysisResultDao.setSession(session);
		abstractSampleDao.setSession(session);

		for (AbstractSample as : samples) {

			AbstractSample asFromDb = abstractSampleDao.findById(as.getId(), false);

			for (AnalysisResult ar : asFromDb.getAnalysisResults()) {

				ar.setState(newState);
				analysisResultDao.makePersistent(ar);

				for (ParameterResult pr : ar.getParameterResults()) {
					pr.setState(newState);
					parameterResultDao.makePersistent(pr);
				}
			}
			asFromDb.setState(newState);
			abstractSampleDao.makePersistent(asFromDb);
		}

		session.getTransaction().commit();

		session.flush();
		session.close();

	}

}
